package com.ngrok.client;

import com.ngrok.BytesUtil;
import com.ngrok.NgrokConfig;
import com.ngrok.NgrokContext;
import com.ngrok.handler.*;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import io.netty.util.HashedWheelTimer;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by pj on 2017/9/27.
 */
//@ConfigurationProperties(prefix = "ngrok")
public class NgrokClient extends NettyClient implements InitializingBean{

    private static   Map<String,Channel> ChannelMap=new HashMap<>();

    public  static Channel newChannel;

    public  NgrokClient localhost;

    private  NgrokConfig ngrokConfig;

    public NgrokClient(NgrokConfig ngrokConfig){
        this();
        this.ngrokConfig=ngrokConfig;
    }

    public NgrokClient(EventLoopGroup group) {
        super(group);
    }

    public NgrokClient(){
        this(new NioEventLoopGroup());
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        NgrokContext context=new NgrokContext(this,ngrokConfig);
        open(new ChannelInitializer() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                SSLEngine sslEngine=getSslContext().newEngine(channel.alloc());
                sslEngine.setEnabledProtocols(ngrokConfig.getProtocol());
                sslEngine.setUseClientMode(true);
                channel.pipeline().addFirst("SSL",new SslHandler(sslEngine));
                channel.pipeline().addLast(new NgrokSocketDecoder());
                channel.pipeline().addLast(new ReadTimeoutHandler( 5));
                channel.pipeline().addLast(new NgrokHttpHandler(context));
                channel.pipeline().addLast(new SocketHandler(context));
                channel.pipeline().addLast(new WriteTimeoutHandler( 5));
            }
        });
        this.localhost=new NgrokClient(this.getGroup());
        this.localhost.open(new ChannelInitializer() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                channel.pipeline().addLast(new LocalHttpHandler(context));
            }
        });
        init();
    }

    public Channel connect(String key, String host, int prot){
        if(ChannelMap.containsKey(key)){
            Channel channel=ChannelMap.get(key);
            synchronized (channel){
                return channel;
            }
        }else{
            Channel channel=this.connect(host,prot);
            ChannelMap.put(key,channel);
            return channel;
        }
    }
    public Channel connect(String key){
        Channel channel=ChannelMap.get(key);
        if(channel!=null){
            return channel;
        }
        return null;
    }

    public void init(){
        Channel channel=this.connect(ngrokConfig.getApi().getServer(),ngrokConfig.getApi().getProt());
        String json = "{\"Type\": \"Auth\", \"Payload\": {\"OS\": \"darwin\", \"ClientId\": \"\", \"MmVersion\": \"2.1\", \"Version\": \"2\", \"User\": \"user\", \"Password\": \"\", \"Arch\": \"amd64\"}}";
        byte[] lenbuf1 = BytesUtil.longToBytes(json.length(), 0);
        byte[] xx1 = json.getBytes();
        byte[] msgpack1 = BytesUtil.addBytesnew(json.length() + 8, lenbuf1, xx1);
        channel.writeAndFlush(Unpooled.copiedBuffer(msgpack1));
    }

    public SslContext getSslContext(){
        SslContext sslContext= null;
        try {
            sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
        } catch (SSLException e) {
            e.printStackTrace();
        }
        return sslContext;
    }

    public  Map<String, Channel> getChannelMap() {
        return ChannelMap;
    }
}
